//========= Copyright ?1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <tier1/KeyValues.h>

#include <vgui/IPanel.h>
#include <vgui/IInputInternal.h>
#include <vgui/ISurface.h>
#include <vgui/IVGUI.h>

#include "Controls.h"
#include "MenuItem.h"
#include "MenuButton.h"
#include "Menu.h"
#include "TextImage.h"
#include "ScrollBar.h"

// memdbgon must be the last include file in a .cpp file
#include "tier0/memdbgon.h"
#define MENU_SEPARATOR_HEIGHT 3

#include "wctype.h"

using namespace vgui2;

//-----------------------------------------------------------------------------
// Purpose: divider line in a menu
//-----------------------------------------------------------------------------
class vgui2::MenuSeparator : public Panel
{
public:
	DECLARE_CLASS_SIMPLE(MenuSeparator, Panel);

	MenuSeparator(Panel *parent, char const *panelName) :
		BaseClass(parent, panelName)
	{
		SetPaintEnabled(true);
		SetPaintBackgroundEnabled(true);
		SetPaintBorderEnabled(false);
	}

	virtual void Paint()
	{
		int w, h;
		GetSize(w, h);

		surface()->DrawSetColor(GetFgColor());
		surface()->DrawFilledRect(4, 1, w - 1, 2);
	}

	virtual void ApplySchemeSettings(IScheme *pScheme)
	{
		BaseClass::ApplySchemeSettings(pScheme);

		SetFgColor(pScheme->GetColor("Menu.SeparatorColor", Color(142, 142, 142, 255)));
		SetBgColor(pScheme->GetColor("Menu.BgColor", Color(0, 0, 0, 255)));
	}
};

DECLARE_BUILD_FACTORY(Menu);

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
Menu::Menu(Panel *parent, const char *panelName) : Panel(parent, panelName)
{
	m_Alignment = Label::a_west;
	m_iFixedWidth = 0;
	m_iMinimumWidth = 0;
	m_iNumVisibleLines = -1; // No limit
	m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
	m_pScroller = new ScrollBar(this, "MenuScrollBar", true);
	m_pScroller->SetVisible(false);
	m_pScroller->AddActionSignalTarget(this);
	_sizedForScrollBar = false;
	SetZPos(1);
	SetVisible(false);
	MakePopup(false);
	SetParent(parent);
	_recalculateWidth = true;
	m_iInputMode = MOUSE;
	m_iCheckImageWidth = 0;
	m_iActivatedItem = 0;

	m_bUseFallbackFont = false;
	m_hFallbackItemFont = INVALID_FONT;
	m_bImageBackground = false;

	if (IsProportional())
	{
		m_iMenuItemHeight = scheme()->GetProportionalScaledValueEx(GetScheme(), DEFAULT_MENU_ITEM_HEIGHT);
	}
	else
	{
		m_iMenuItemHeight = DEFAULT_MENU_ITEM_HEIGHT;
	}
	m_hItemFont = INVALID_FONT;
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
Menu::~Menu()
{
	delete m_pScroller;
}

//-----------------------------------------------------------------------------
// Purpose: Remove all menu items from the menu. 
//-----------------------------------------------------------------------------
void Menu::DeleteAllItems()
{
	FOR_EACH_LL(m_MenuItems, i)
	{
		m_MenuItems[i]->MarkForDeletion();
	}

	m_MenuItems.RemoveAll();
	m_SortedItems.RemoveAll();
	m_VisibleSortedItems.RemoveAll();
	m_Separators.RemoveAll();
	int c = m_SeparatorPanels.Count();
	for (int i = 0; i < c; ++i)
	{
		m_SeparatorPanels[i]->MarkForDeletion();
	}
	m_SeparatorPanels.RemoveAll();
	InvalidateLayout();
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(MenuItem *panel)
{
	panel->SetParent(this);
	MEM_ALLOC_CREDIT();
	int itemID = m_MenuItems.AddToTail(panel);
	m_SortedItems.AddToTail(itemID);
	InvalidateLayout(false);
	_recalculateWidth = true;
	panel->SetContentAlignment(m_Alignment);
	if (INVALID_FONT != m_hItemFont)
	{
		panel->SetFont(m_hItemFont);
	}
	if (m_bUseFallbackFont && INVALID_FONT != m_hFallbackItemFont)
	{
		Label *l = panel;
		TextImage *ti = l->GetTextImage();
		if (ti)
		{
			ti->SetUseFallbackFont(m_bUseFallbackFont, m_hFallbackItemFont);
		}
	}
	return itemID;
}


//-----------------------------------------------------------------------------
// Remove a single item
//-----------------------------------------------------------------------------
void Menu::DeleteItem(int itemID)
{
	// FIXME: This doesn't work with separator panels yet
	Assert(m_SeparatorPanels.Count() == 0);

	m_MenuItems[itemID]->MarkForDeletion();
	m_MenuItems.Remove(itemID);

	m_SortedItems.FindAndRemove(itemID);
	m_VisibleSortedItems.FindAndRemove(itemID);

	InvalidateLayout(false);
	_recalculateWidth = true;
}


//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input  : *item - MenuItem 
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
//			*userData - any user data associated with this menu item
// Output:  itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItemCharCommand(MenuItem *item, const char *command, Panel *target, const KeyValues *userData)
{
	item->SetCommand(command);
	item->AddActionSignalTarget(target);
	item->SetUserData(userData);
	return AddMenuItem(item);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu. 
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
// Output:  itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItemKeyValuesCommand(MenuItem *item, KeyValues *message, Panel *target, const KeyValues *userData)
{
	item->SetCommand(message);
	item->AddActionSignalTarget(target);
	item->SetUserData(userData);
	return AddMenuItem(item);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu.
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
// Output:  itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText);
	return AddMenuItemCharCommand(item, command, target, userData);
}

int Menu::AddMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText);
	return AddMenuItemCharCommand(item, command, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
// Output:  itemID - ID of this item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
	return AddMenuItem(itemText, itemText, command, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu. 
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

int Menu::AddMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	return AddMenuItem(itemText, itemText, message, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be the text of the command sent when the
//						item is selected.
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddMenuItem(const char *itemText, Panel *target, const KeyValues *userData)
{
	return AddMenuItem(itemText, itemText, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu.
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
	return AddMenuItemCharCommand(item, command, target, userData);
}

int Menu::AddCheckableMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
	return AddMenuItemCharCommand(item, command, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, const char *command, Panel *target, const KeyValues *userData)
{
	return AddCheckableMenuItem(itemText, itemText, command, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu. 
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText, NULL, true);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

int Menu::AddCheckableMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText, NULL, true);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, KeyValues *message, Panel *target, const KeyValues *userData)
{
	return AddCheckableMenuItem(itemText, itemText, message, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a checkable menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be the text of the command sent when the
//						item is selected.
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCheckableMenuItem(const char *itemText, Panel *target, const KeyValues *userData)
{
	return AddCheckableMenuItem(itemText, itemText, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu.
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemName, const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu);
	return AddMenuItemCharCommand(item, command, target, userData);
}

int Menu::AddCascadingMenuItem(const char *itemName, const wchar_t *wszItemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu);
	return AddMenuItemCharCommand(item, command, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*command -  Command text to be sent when menu item is selected	
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, const char *command, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	return AddCascadingMenuItem(itemText, itemText, command, target, cascadeMenu, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu. 
// Input  : *itemName - Name of item 
//			*itemText - Name of item text that will appear in the manu.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemName, const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, itemText, cascadeMenu);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

int Menu::AddCascadingMenuItem(const char *itemName, const wchar_t *wszItemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	MenuItem *item = new MenuItem(this, itemName, wszItemText, cascadeMenu);
	return AddMenuItemKeyValuesCommand(item, message, target, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be used as the name of the menu item panel.
//			*message - pointer to the message to send when the item is selected
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, KeyValues *message, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	return AddCascadingMenuItem(itemText, itemText, message, target, cascadeMenu, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Add a Cascading menu item to the menu. 
// Input  : *itemText - Name of item text that will appear in the manu.
//						This will also be the text of the command sent when the
//						item is selected.
//			*target - Target panel of the command
//          *cascadeMenu - if the menu item opens a cascading menu, this is a 
//							ptr to the menu that opens on selecting the item
//-----------------------------------------------------------------------------
int Menu::AddCascadingMenuItem(const char *itemText, Panel *target, Menu *cascadeMenu, const KeyValues *userData)
{
	return AddCascadingMenuItem(itemText, itemText, target, cascadeMenu, userData);
}

//-----------------------------------------------------------------------------
// Purpose: Sets the values of a menu item at the specified index
// Input  : index - the index of this item entry
//			*message - pointer to the message to send when the item is selected
//-----------------------------------------------------------------------------
void Menu::UpdateMenuItem(int itemID, const char *itemText, KeyValues *message, const KeyValues *userData)
{
	Assert(m_MenuItems.IsValidIndex(itemID));
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		// make sure its enabled since disabled items get highlighted.
		if (menuItem)
		{
			menuItem->SetText(itemText);
			menuItem->SetCommand(message);
			if (userData)
			{
				menuItem->SetUserData(userData);
			}
		}
	}
	_recalculateWidth = true;
}


//-----------------------------------------------------------------------------
// Purpose: Sets the values of a menu item at the specified index
//-----------------------------------------------------------------------------
void Menu::UpdateMenuItem(int itemID, const wchar_t *wszItemText, KeyValues *message, const KeyValues *userData)
{
	Assert(m_MenuItems.IsValidIndex(itemID));
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		// make sure its enabled since disabled items get highlighted.
		if (menuItem)
		{
			menuItem->SetText(wszItemText);
			menuItem->SetCommand(message);
			if (userData)
			{
				menuItem->SetUserData(userData);
			}
		}
	}
	_recalculateWidth = true;
}


//-----------------------------------------------------------------------------
// Sets the content alignment of all items in the menu
//-----------------------------------------------------------------------------
void Menu::SetContentAlignment(Label::Alignment alignment)
{
	if (m_Alignment != alignment)
	{
		m_Alignment = alignment;

		// Change the alignment of existing menu items
		int nCount = m_MenuItems.Count();
		for (int i = 0; i < nCount; ++i)
		{
			m_MenuItems[i]->SetContentAlignment(alignment);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose: Locks down a specific width
//-----------------------------------------------------------------------------
void Menu::SetFixedWidth(int width)
{
	// the padding makes it so the menu has the label padding on each side of the menu.
	// makes the menu items look centered.
	m_iFixedWidth = width;
	InvalidateLayout(false);
}

//-----------------------------------------------------------------------------
// Purpose: sets the height of each menu item
//-----------------------------------------------------------------------------
void Menu::SetMenuItemHeight(int itemHeight)
{
	m_iMenuItemHeight = itemHeight;
}

int  Menu::GetMenuItemHeight() const
{
	return m_iMenuItemHeight;
}

int Menu::CountVisibleItems()
{
	int count = 0;
	int c = m_SortedItems.Count();
	for (int i = 0; i < c; ++i)
	{
		if (m_MenuItems[m_SortedItems[i]]->IsVisible())
			++count;
	}
	return count;
}

void Menu::ComputeWorkspaceSize(int& workWide, int& workTall)
{
	// make sure we factor in insets
	int ileft, iright, itop, ibottom;
	GetInset(ileft, iright, itop, ibottom);

	int workX, workY;
	surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);
	workTall -= 20;
	workTall -= itop;
	workTall -= ibottom;
}

// Assumes relative coords in screenspace
void Menu::PositionRelativeToPanel(Panel *relative, MenuDirection_e direction, int nAdditionalYOffset /*=0*/, bool showMenu /*=false*/)
{
	Assert(relative);
	int rx, ry, rw, rh;
	relative->GetBounds(rx, ry, rw, rh);
	relative->LocalToScreen(rx, ry);

	if (direction == CURSOR)
	{
		// force the menu to appear where the mouse button was pressed
		input()->GetCursorPos(rx, ry);
		rw = rh = 0;
	}
	else if (direction == ALIGN_WITH_PARENT && relative->GetVParent())
	{
		rx = 0, ry = 0;
		relative->ParentLocalToScreen(rx, ry);
		rx -= 1; // take border into account
		ry += rh + nAdditionalYOffset;
		rw = rh = 0;
	}
	else
	{
		rx = 0, ry = 0;
		relative->LocalToScreen(rx, ry);
	}

	int workWide, workTall;
	ComputeWorkspaceSize(workWide, workTall);

	// Final pos
	int x = 0, y = 0;

	int mWide, mTall;
	GetSize(mWide, mTall);

	switch (direction)
	{
	case Menu::UP: // Menu prefers to open upward
	{
		x = rx;
		int topOfReference = ry;
		y = topOfReference - mTall;
		if (y < 0)
		{
			int bottomOfReference = ry + rh + 1;
			int remainingPixels = workTall - bottomOfReference;

			// Can't fit on bottom, either, move to side
			if (mTall >= remainingPixels)
			{
				y = workTall - mTall;
				x = rx + rw;
				// Try and place it to the left of the button
				if (x + mWide > workWide)
				{
					x = rx - mWide;
				}
			}
			else
			{
				// Room at bottom
				y = bottomOfReference;
			}
		}
	}
	break;
	// Everyone else aligns downward...
	default:
	case Menu::LEFT:
	case Menu::RIGHT:
	case Menu::DOWN:
	{
		x = rx;
		int bottomOfReference = ry + rh + 1;
		y = bottomOfReference;
		if (bottomOfReference + mTall >= workTall)
		{
			// See if there's run straight above
			if (mTall >= ry) // No room, try and push menu to right or left
			{
				y = workTall - mTall;
				x = rx + rw;
				// Try and place it to the left of the button
				if (x + mWide > workWide)
				{
					x = rx - mWide;
				}
			}
			else
			{
				// Room at top
				y = ry - mTall;
			}
		}
	}
	break;
	}

	// Check left rightness
	if (x + mWide > workWide)
	{
		x = workWide - mWide;
		Assert(x >= 0); // yikes!!!
	}
	else if (x < 0)
	{
		x = 0;
	}

	SetPos(x, y);
	if (showMenu)
	{
		SetVisible(true);
	}
}

int Menu::ComputeFullMenuHeightWithInsets()
{
	// make sure we factor in insets
	int ileft, iright, itop, ibottom;
	GetInset(ileft, iright, itop, ibottom);

	int separatorHeight = 3;

	// add up the size of all the child panels
	// move the child panels to the correct place in the menu
	int totalTall = itop + ibottom;
	int i;
	for (i = 0; i < m_SortedItems.Count(); i++)		// use sortedItems instead of MenuItems due to SetPos()
	{
		int itemId = m_SortedItems[i];

		MenuItem *child = m_MenuItems[itemId];
		Assert(child);
		if (!child)
			continue;
		// These should all be visible at this point
		if (!child->IsVisible())
			continue;

		totalTall += m_iMenuItemHeight;

		// Add a separator if needed...
		int sepIndex = m_Separators.Find(itemId);
		if (sepIndex != m_Separators.InvalidIndex())
		{
			totalTall += separatorHeight;
		}
	}

	return totalTall;
}

//-----------------------------------------------------------------------------
// Purpose: Reformat according to the new layout
//-----------------------------------------------------------------------------
void Menu::PerformLayout()
{
	MenuItem *parent = GetParentMenuItem();
	bool cascading = parent != NULL ? true : false;

	// make sure we factor in insets
	int ileft, iright, itop, ibottom;
	GetInset(ileft, iright, itop, ibottom);

	int workWide, workTall;

	ComputeWorkspaceSize(workWide, workTall);

	int fullHeightWouldRequire = ComputeFullMenuHeightWithInsets();

	bool bNeedScrollbar = fullHeightWouldRequire >= workTall;

	int maxVisibleItems = CountVisibleItems();

	if (m_iNumVisibleLines > 0 &&
		maxVisibleItems > m_iNumVisibleLines)
	{
		bNeedScrollbar = true;
		maxVisibleItems = m_iNumVisibleLines;
	}

	// if we have a scroll bar
	if (bNeedScrollbar)
	{
		// add it to the display
		AddScrollBar();

		// This fills in m_VisibleSortedItems as needed
		MakeItemsVisibleInScrollRange(m_iNumVisibleLines, min(fullHeightWouldRequire, workTall));
	}
	else
	{
		RemoveScrollBar();
		// Make everything visible
		m_VisibleSortedItems.RemoveAll();
		int i;
		int c = m_SortedItems.Count();
		for (i = 0; i < c; ++i)
		{
			int itemID = m_SortedItems[i];
			MenuItem *child = m_MenuItems[itemID];
			if (!child || !child->IsVisible())
				continue;

			m_VisibleSortedItems.AddToTail(itemID);
		}

		// Hide the separators, the needed ones will be readded below
		c = m_SeparatorPanels.Count();
		for (i = 0; i < c; ++i)
		{
			if (m_SeparatorPanels[i])
			{
				m_SeparatorPanels[i]->SetVisible(false);
			}
		}
	}

	// get the appropriate menu border
	LayoutMenuBorder();

	int trueW = GetWide();
	if (bNeedScrollbar)
	{
		trueW -= m_pScroller->GetWide();
	}
	int separatorHeight = MENU_SEPARATOR_HEIGHT;

	// add up the size of all the child panels
	// move the child panels to the correct place in the menu
	int menuTall = 0;
	int totalTall = itop + ibottom;
	int i;
	for (i = 0; i < m_VisibleSortedItems.Count(); i++)		// use sortedItems instead of MenuItems due to SetPos()
	{
		int itemId = m_VisibleSortedItems[i];

		MenuItem *child = m_MenuItems[itemId];
		Assert(child);
		if (!child)
			continue;
		// These should all be visible at this point
		if (!child->IsVisible())
			continue;

		if (totalTall >= workTall)
			break;

		if (INVALID_FONT != m_hItemFont)
		{
			child->SetFont(m_hItemFont);
		}

		// take into account inset
		child->SetPos(0, menuTall);
		child->SetTall(m_iMenuItemHeight); // Width is set in a second pass
		menuTall += m_iMenuItemHeight;
		totalTall += m_iMenuItemHeight;

		// this will make all the menuitems line up in a column with space for the checks to the left.
		if ((!child->IsCheckable()) && (m_iCheckImageWidth > 0))
		{
			// Non checkable items have to move over
			child->SetTextInset(m_iCheckImageWidth, 0);
		}
		else if (child->IsCheckable())
		{
			child->SetTextInset(0, 0); //TODO: for some reason I can't comment this out.
		}

		// Add a separator if needed...
		int sepIndex = m_Separators.Find(itemId);
		if (sepIndex != m_Separators.InvalidIndex())
		{
			MenuSeparator *sep = m_SeparatorPanels[sepIndex];
			Assert(sep);
			sep->SetVisible(true);
			sep->SetBounds(0, menuTall, trueW, separatorHeight);
			menuTall += separatorHeight;
			totalTall += separatorHeight;
		}
	}

	if (!m_iFixedWidth)
	{
		_recalculateWidth = true;
		CalculateWidth();
	}
	else if (m_iFixedWidth)
	{
		_menuWide = m_iFixedWidth;
		// fixed width menus include the scroll bar in their width.
		if (_sizedForScrollBar)
		{
			_menuWide -= m_pScroller->GetWide();
		}
	}

	SizeMenuItems();

	int extraWidth = 0;
	if (_sizedForScrollBar)
	{
		extraWidth = m_pScroller->GetWide();
	}

	int mwide = _menuWide + extraWidth;
	if (mwide > workWide)
	{
		mwide = workWide;
	}
	int mtall = menuTall + itop + ibottom;
	if (mtall > workTall)
	{
		// Shouldn't happen
		mtall = workTall;
	}

	// set the new size of the menu
	SetSize(mwide, mtall);

	// move the menu to the correct position if it is a cascading menu.
	if (cascading)
	{
		// move the menu to the correct position if it is a cascading menu.
		PositionCascadingMenu();
	}

	// set up scroll bar as appropriate
	if (m_pScroller->IsVisible())
	{
		LayoutScrollBar();
	}

	FOR_EACH_LL(m_MenuItems, j)
	{
		m_MenuItems[j]->InvalidateLayout(); // cause each menu item to redo its apply settings now we have sized ourselves
	}

	Repaint();
}


//-----------------------------------------------------------------------------
// Purpose: Force the menu to work out how wide it should be
//-----------------------------------------------------------------------------
void Menu::ForceCalculateWidth()
{
	_recalculateWidth = true;
	CalculateWidth();
	PerformLayout();
}

//-----------------------------------------------------------------------------
// Purpose: Figure out how wide the menu should be if the menu is not fixed width
//-----------------------------------------------------------------------------
void Menu::CalculateWidth()
{
	if (!_recalculateWidth)
		return;

	_menuWide = 0;
	if (!m_iFixedWidth)
	{
		// find the biggest menu item
		FOR_EACH_LL(m_MenuItems, i)
		{
			int wide, tall;
			m_MenuItems[i]->GetContentSize(wide, tall);
			if (wide > _menuWide - Label::Content)
			{
				_menuWide = wide + Label::Content;
			}
		}
	}

	// enfoce a minimumWidth 
	if (_menuWide < m_iMinimumWidth)
	{
		_menuWide = m_iMinimumWidth;
	}

	_recalculateWidth = false;
}

//-----------------------------------------------------------------------------
// Purpose: Set up the scroll bar attributes,size and location.
//-----------------------------------------------------------------------------
void Menu::LayoutScrollBar()
{
	//!! need to make it recalculate scroll positions
	m_pScroller->SetEnabled(false);
	m_pScroller->SetRangeWindow(m_VisibleSortedItems.Count());
	m_pScroller->SetRange(0, CountVisibleItems());
	m_pScroller->SetButtonPressedScrollValue(1);

	int wide, tall;
	GetSize(wide, tall);

	// make sure we factor in insets
	int ileft, iright, itop, ibottom;
	GetInset(ileft, iright, itop, ibottom);

	// with a scroll bar we take off the inset
	wide -= iright;

	m_pScroller->SetPos(wide - m_pScroller->GetWide(), 1);

	// scrollbar is inside the menu's borders.
	m_pScroller->SetSize(m_pScroller->GetWide(), tall - ibottom - itop);

}

//-----------------------------------------------------------------------------
// Purpose: Figure out where to open menu if it is a cascading menu
//-----------------------------------------------------------------------------
void Menu::PositionCascadingMenu()
{
	Assert(GetVParent());
	int parentX, parentY, parentWide, parentTall;
	// move the menu to the correct place below the menuItem
	ipanel()->GetSize(GetVParent(), parentWide, parentTall);
	ipanel()->GetPos(GetVParent(), parentX, parentY);

	parentX += parentWide, parentY = 0;

	ParentLocalToScreen(parentX, parentY);

	SetPos(parentX, parentY);

	// for cascading menus, 
	// make sure we're on the screen
	int workX, workY, workWide, workTall, x, y, wide, tall;
	GetBounds(x, y, wide, tall);
	surface()->GetWorkspaceBounds(workX, workY, workWide, workTall);

	if (x + wide > workX + workWide)
	{
		// we're off the right, move the menu to the left side
		// orignalX - width of the parentmenuitem - width of this menu.
		// add 2 pixels to offset one pixel onto the parent menu.
		x -= (parentWide + wide);
		x -= 2;
	}
	else
	{
		// alignment move it in the amount of the insets.
		x += 1;
	}

	if (y + tall > workY + workTall)
	{
		int lastWorkY = workY + workTall;
		int pixelsOffBottom = (y + tall) - lastWorkY;

		y -= pixelsOffBottom;
		y -= 2;
	}
	else
	{
		y -= 1;
	}
	SetPos(x, y);

	MoveToFront();
}

//-----------------------------------------------------------------------------
// Purpose: Size the menu items so they are the width of the menu.
//			Also size the menu items with cascading menus so the arrow fits in there.
//-----------------------------------------------------------------------------
void Menu::SizeMenuItems()
{
	int ileft, iright, itop, ibottom;
	GetInset(ileft, iright, itop, ibottom);

	// assign the sizes of all the menu item panels
	FOR_EACH_LL(m_MenuItems, i)
	{
		MenuItem *child = m_MenuItems[i];
		if (child)
		{
			// labels do thier own sizing. this will size the label to the width of the menu,
			// this will put the cascading menu arrow on the right side automatically.	
			child->SetWide(_menuWide - ileft - iright);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Makes menu items visible in relation to where the scroll bar is
//-----------------------------------------------------------------------------
void Menu::MakeItemsVisibleInScrollRange(int maxVisibleItems, int nNumPixelsAvailable)
{
	// Detach all items from tree
	int i;
	FOR_EACH_LL(m_MenuItems, item)
	{
		m_MenuItems[item]->SetBounds(0, 0, 0, 0);
	}
	for (i = 0; i < m_SeparatorPanels.Count(); ++i)
	{
		m_SeparatorPanels[i]->SetVisible(false);
	}

	m_VisibleSortedItems.RemoveAll();

	int tall = 0;

	int startItem = m_pScroller->GetValue();
	Assert(startItem >= 0);
	do
	{
		if (startItem >= m_SortedItems.Count())
			break;

		int itemId = m_SortedItems[startItem];

		if (!m_MenuItems[itemId]->IsVisible())
		{
			++startItem;
			continue;
		}

		int itemHeight = m_iMenuItemHeight;
		int sepIndex = m_Separators.Find(itemId);
		if (sepIndex != m_Separators.InvalidIndex())
		{
			itemHeight += MENU_SEPARATOR_HEIGHT;
		}

		if (tall + itemHeight > nNumPixelsAvailable)
			break;

		// Too many items
		if (maxVisibleItems > 0)
		{
			if (m_VisibleSortedItems.Count() >= maxVisibleItems)
				break;
		}

		tall += itemHeight;
		// Re-attach this one
		m_VisibleSortedItems.AddToTail(itemId);
		++startItem;
	} while (true);
}

//-----------------------------------------------------------------------------
// Purpose: Get the approproate menu border
//-----------------------------------------------------------------------------
void Menu::LayoutMenuBorder()
{
	IBorder *menuBorder;
	IScheme *pScheme = scheme()->GetIScheme(GetScheme());

	menuBorder = pScheme->GetBorder("MenuBorder");

	if (menuBorder)
	{
		SetBorder(menuBorder);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Draw a black border on the right side of the menu items
//-----------------------------------------------------------------------------
void Menu::Paint()
{
	if (m_pScroller->IsVisible())
	{
		// draw black bar
		int wide, tall;
		GetSize(wide, tall);
		surface()->DrawSetColor(_borderDark);
		if (IsProportional())
		{
			surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
		}
		else
		{
			surface()->DrawFilledRect(wide - m_pScroller->GetWide(), -1, wide - m_pScroller->GetWide() + 1, tall);
		}
	}
}


//-----------------------------------------------------------------------------
// Purpose:	sets the max number of items visible (scrollbar appears with more)
// Input  : numItems - 
//-----------------------------------------------------------------------------
void Menu::SetNumberOfVisibleItems(int numItems)
{
	m_iNumVisibleLines = numItems;
	InvalidateLayout(false);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
MenuItem *Menu::GetMenuItem(int itemID)
{
	if (!m_MenuItems.IsValidIndex(itemID))
		return NULL;

	return m_MenuItems[itemID];
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool Menu::IsValidMenuID(int itemID)
{
	return m_MenuItems.IsValidIndex(itemID);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int Menu::GetInvalidMenuID()
{
	return m_MenuItems.InvalidIndex();
}

//-----------------------------------------------------------------------------
// Purpose: When a menuItem is selected, close cascading menus
//          if the menuItem selected has a cascading menu attached, we
//          want to keep that one open so skip it.
//          Passing NULL will close all cascading menus.
//-----------------------------------------------------------------------------
void Menu::CloseOtherMenus(MenuItem *item)
{
	FOR_EACH_LL(m_MenuItems, i)
	{
		if (m_MenuItems[i] == item)
			continue;

		m_MenuItems[i]->CloseCascadeMenu();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Respond to string commands.
//-----------------------------------------------------------------------------
void Menu::OnCommand(const char *command)
{
	// forward on the message
	PostActionSignal(new KeyValues("Command", "command", command));

	Panel::OnCommand(command);
}

//-----------------------------------------------------------------------------
// Purpose: Handle key presses, Activate shortcuts
//-----------------------------------------------------------------------------
void Menu::OnKeyCodeTyped(KeyCode code)
{
	// Don't allow key inputs when disabled!
	if (!IsEnabled())
		return;

	bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT));
	if (alt)
	{
		BaseClass::OnKeyCodeTyped(code);
		PostActionSignal(new KeyValues("MenuClose"));
	}

	switch (code)
	{
	case KEY_ESCAPE:
	{
		// hide the menu on ESC
		SetVisible(false);
		break;
	}
	// arrow keys scroll through items on the list.
	// they should also scroll the scroll bar if needed
	case KEY_UP:
	{
		MoveAlongMenuItemList(MENU_UP, 0);
		if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
		{
			m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
		}
		break;
	}
	case KEY_DOWN:
	{
		MoveAlongMenuItemList(MENU_DOWN, 0);
		if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
		{
			m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
		}
		break;
	}
	// for now left and right arrows just open or close submenus if they are there.
	case KEY_RIGHT:
	{
		// make sure a menuItem is currently selected
		if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
		{
			if (m_MenuItems[m_iCurrentlySelectedItemID]->HasMenu())
			{
				ActivateItem(m_iCurrentlySelectedItemID);
			}
			else
			{
				BaseClass::OnKeyCodeTyped(code);
			}
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code);
		}
		break;
	}
	case KEY_LEFT:
	{
		// if our parent is a menu item then we are a submenu so close us.
		if (GetParentMenuItem())
		{
			SetVisible(false);
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code);
		}
		break;
	}
	case KEY_ENTER:
	{
		// make sure a menuItem is currently selected
		if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
		{
			ActivateItem(m_iCurrentlySelectedItemID);
		}
		else
		{
			BaseClass::OnKeyCodeTyped(code); // chain up
		}
		break;
	}
	}

	// don't chain back
}


//-----------------------------------------------------------------------------
// Purpose: Handle key presses, Activate shortcuts
// Input  : code - 
//-----------------------------------------------------------------------------
void Menu::OnKeyTyped(wchar_t unichar)
{
	//
	// NOTE - if hotkeys are ever enabled you need to work out a way to differentiate between
	// combo box menus (which can't have hot keys) and system style menus (which do have hot keys).
	//
	//
	/*	if (unichar)
	{
	// iterate the menu items looking for one with the matching hotkey
	FOR_EACH_LL( m_MenuItems, i )
	{
	MenuItem *panel = m_MenuItems[i];
	if (panel->IsVisible())
	{
	Panel *hot = panel->HasHotkey(unichar);
	if (hot)
	{
	// post a message to the menuitem telling it it's hotkey was pressed
	PostMessage(hot, new KeyValues("Hotkey"));
	return;
	}
	// if the menuitem is a cascading menuitem and it is open, check its hotkeys too
	Menu *cascadingMenu = panel->GetMenu();
	if (cascadingMenu && cascadingMenu->IsVisible())
	{
	cascadingMenu->OnKeyTyped(unichar);
	}
	}
	}
	}
	*/

	int itemToSelect = m_iCurrentlySelectedItemID;
	if (itemToSelect < 0)
	{
		itemToSelect = 0;
	}

	int i;
	wchar_t menuItemName[255];

	i = itemToSelect + 1;
	if (i >= m_MenuItems.Count())
	{
		i = 0;
	}

	while (i != itemToSelect)
	{
		m_MenuItems[i]->GetText(menuItemName, 254);

		if (towlower(menuItemName[0]) == towlower(unichar))
		{
			itemToSelect = i;
			break;
		}

		i++;
		if (i >= m_MenuItems.Count())
		{
			i = 0;
		}
	}

	if (itemToSelect >= 0)
	{
		SetCurrentlyHighlightedItem(itemToSelect);
		InvalidateLayout();
	}

	// don't chain back
}

//-----------------------------------------------------------------------------
// Purpose: Handle the mouse wheel event, scroll the selection
//-----------------------------------------------------------------------------
void Menu::OnMouseWheeled(int delta)
{
	if (!m_pScroller->IsVisible())
		return;

	int val = m_pScroller->GetValue();
	val -= delta;

	m_pScroller->SetValue(val);

	// moving the slider redraws the scrollbar,
	// and so we should redraw the menu since the
	// menu draws the black border to the right of the scrollbar.
	InvalidateLayout();

	// don't chain back
}


//-----------------------------------------------------------------------------
// Purpose: Lose focus, hide menu
//-----------------------------------------------------------------------------
void Menu::OnKillFocus()
{
	// check to see if it's a child taking it
	if (!input()->GetFocus() || !ipanel()->HasParent(input()->GetFocus(), GetVPanel()))
	{
		// if we don't accept keyboard input, then we have to ignore the killfocus if it's not actually being stolen
		if (!IsKeyBoardInputEnabled() && !input()->GetFocus())
			return;

		// get the parent of this menu. 
		MenuItem *item = GetParentMenuItem();
		// if the parent is a menu item, this menu is a cascading menu
		// if the panel that is getting focus is the parent menu, don't close this menu.
		if ((item) && (input()->GetFocus() == item->GetVParent()))
		{
			// if we are in mouse mode and we clicked on the menuitem that
			// triggers the cascading menu, leave it open.
			if (m_iInputMode == MOUSE)
			{
				// return the focus to the cascading menu.
				MoveToFront();
				return;
			}
		}

		// forward the message to the parent.
		PostActionSignal(new KeyValues("MenuClose"));

		// hide this menu
		SetVisible(false);
	}

}

namespace vgui2
{

	class CMenuManager
	{
	public:
		void AddMenu(Menu *m)
		{
			if (!m)
				return;

			int c = m_Menus.Count();
			for (int i = 0; i < c; ++i)
			{
				if (m_Menus[i].Get() == m)
					return;
			}

			DHANDLE< Menu > h;
			h = m;
			m_Menus.AddToTail(h);
		}

		void RemoveMenu(Menu *m)
		{
			if (!m)
				return;

			int c = m_Menus.Count();
			for (int i = c - 1; i >= 0; --i)
			{
				if (m_Menus[i].Get() == m)
				{
					m_Menus.Remove(i);
					return;
				}
			}
		}

		void OnInternalMousePressed(Panel *other, MouseCode code)
		{
			int c = m_Menus.Count();
			if (!c)
				return;

			int x, y;
			input()->GetCursorPos(x, y);

			bool mouseInsideMenuRelatedPanel = false;

			for (int i = c - 1; i >= 0; --i)
			{
				Menu *m = m_Menus[i].Get();
				if (!m)
				{
					m_Menus.Remove(i);
					continue;
				}

				// See if the mouse is within a menu
				if (IsWithinMenuOrRelative(m, x, y))
				{
					mouseInsideMenuRelatedPanel = true;
				}
			}

			if (mouseInsideMenuRelatedPanel)
			{
				return;
			}

			AbortMenus();
		}

		void AbortMenus()
		{
			// Close all of the menus
			int c = m_Menus.Count();
			for (int i = c - 1; i >= 0; --i)
			{
				Menu *m = m_Menus[i].Get();
				if (!m)
				{
					continue;
				}

				m_Menus.Remove(i);

				// Force it to close
				m->SetVisible(false);
			}

			m_Menus.RemoveAll();
		}

		bool IsWithinMenuOrRelative(Panel *panel, int x, int y)
		{
			VPANEL topMost = panel->IsWithinTraverse(x, y, true);
			if (topMost)
			{
				// It's over the menu
				if (topMost == panel->GetVPanel())
				{
					return true;
				}

				// It's over something which is parented to the menu (i.e., a menu item)
				if (ipanel()->HasParent(topMost, panel->GetVPanel()))
				{
					return true;
				}
			}

			if (panel->GetParent())
			{
				Panel *parent = panel->GetParent();

				topMost = parent->IsWithinTraverse(x, y, true);

				if (topMost)
				{
					if (topMost == parent->GetVPanel())
					{
						return true;
					}

					/*
					// NOTE:  this check used to not cast to MenuButton, but it seems wrong to me
					//  since if the mouse is over another child of the parent panel to the menu then
					//  the menu stays visible.  I think this is bogus.
					Panel *pTopMost = ipanel()->GetPanel(topMost, GetControlsModuleName());

					if ( pTopMost &&
					ipanel()->HasParent( topMost, parent->GetVPanel() ) &&
					dynamic_cast< MenuButton * >( pTopMost ) )
					{
					Msg( "topMost %s has parent %s\n",
					ipanel()->GetName( topMost ),
					parent->GetName() );

					return true;
					}
					*/
				}
			}

			return false;
		}

#ifdef DBGFLAG_VALIDATE
		void Validate(CValidator &validator, char *pchName)
		{
			validator.Push("CMenuManager", this, pchName);
			m_Menus.Validate(validator, "m_Menus");
			validator.Pop();
		}
#endif

	private:

		// List of visible menus
		CUtlVector< DHANDLE< Menu > >	m_Menus;
	};


	// Singleton helper class
	static CMenuManager g_MenuMgr;

	void ValidateMenuGlobals(CValidator &validator)
	{
#ifdef DBGFLAG_VALIDATE
		g_MenuMgr.Validate(validator, "g_MenuMgr");
#endif
	}

}  // end namespace vgui

   //-----------------------------------------------------------------------------
   // Purpose: Static method called on mouse released to see if Menu objects should be aborted
   // Input  : *other - 
   //			code - 
   //-----------------------------------------------------------------------------
void Menu::OnInternalMousePressed(Panel *other, MouseCode code)
{
	g_MenuMgr.OnInternalMousePressed(other, code);
}

//-----------------------------------------------------------------------------
// Purpose: Set visibility of menu and its children as appropriate. 
//-----------------------------------------------------------------------------
void Menu::SetVisible(bool state)
{
	if (state == IsVisible())
		return;

	if (state == false)
	{
		PostActionSignal(new KeyValues("MenuClose"));
		CloseOtherMenus(NULL);

		SetCurrentlySelectedItem(-1);

		g_MenuMgr.RemoveMenu(this);
	}
	else if (state == true)
	{
		MoveToFront();
		RequestFocus();

		g_MenuMgr.AddMenu(this);
	}

	// must be after movetofront()
	BaseClass::SetVisible(state);
	_sizedForScrollBar = false;
}


//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Menu::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	SetFgColor(GetSchemeColor("Menu.TextColor", GetSchemeColor("Menu/FgColor", Color(255, 255, 255, 155), pScheme), pScheme));
	SetBgColor(GetSchemeColor("Menu.BgColor", GetSchemeColor("Menu/BgColor", Color(255, 255, 255, 155), pScheme), pScheme));

	_borderDark = GetSchemeColor("Border.Dark", GetSchemeColor("BorderDark", Color(255, 255, 255, 0), pScheme), pScheme);

	const char *resourceString = pScheme->GetResourceString("Menu/TopLeft");

	if (resourceString[0])
	{
		m_bImageBackground = true;
		m_pTopBackground[0] = scheme()->GetImage(resourceString, true);
		m_pTopBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/TopCenter"), true);
		m_pTopBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/TopRight"), true);
		m_pCenterBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleLeft"), true);
		m_pCenterBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleCenter"), true);
		m_pCenterBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/MiddleRight"), true);
		m_pBottomBackground[0] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomLeft"), true);
		m_pBottomBackground[1] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomCenter"), true);
		m_pBottomBackground[2] = scheme()->GetImage(pScheme->GetResourceString("Menu/BottomRight"), true);
	}

	FOR_EACH_LL(m_MenuItems, i)
	{
		if (m_MenuItems[i]->IsCheckable())
		{
			int wide, tall;
			m_MenuItems[i]->GetCheckImageSize(wide, tall);

			m_iCheckImageWidth = max(m_iCheckImageWidth, wide);
		}
	}
	_recalculateWidth = true;
	CalculateWidth();

	InvalidateLayout();
}

void Menu::SetBgColor(Color newColor)
{
	BaseClass::SetBgColor(newColor);
	FOR_EACH_LL(m_MenuItems, i)
	{
		if (m_MenuItems[i]->HasMenu())
		{
			m_MenuItems[i]->GetMenu()->SetBgColor(newColor);
		}
	}
}

void Menu::SetFgColor(Color newColor)
{
	BaseClass::SetFgColor(newColor);
	FOR_EACH_LL(m_MenuItems, i)
	{
		if (m_MenuItems[i]->HasMenu())
		{
			m_MenuItems[i]->GetMenu()->SetFgColor(newColor);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Menu::SetBorder(class IBorder *border)
{
	Panel::SetBorder(border);
}

//-----------------------------------------------------------------------------
// Purpose: returns a pointer to a MenuItem that is this menus parent, if it has one
//-----------------------------------------------------------------------------
MenuItem *Menu::GetParentMenuItem()
{
	return dynamic_cast<MenuItem *>(GetParent());
}

//-----------------------------------------------------------------------------
// Purpose: Hide the menu when an item has been selected
//-----------------------------------------------------------------------------
void Menu::OnMenuItemSelected(Panel *panel)
{
	SetVisible(false);
	m_pScroller->SetVisible(false);

	// chain this message up through the hierarchy so
	// all the parent menus will close

	// get the parent of this menu. 
	MenuItem *item = GetParentMenuItem();
	// if the parent is a menu item, this menu is a cascading menu
	if (item)
	{
		// get the parent of the menuitem. it should be a menu.
		Menu *parentMenu = item->GetParentMenu();
		if (parentMenu)
		{
			// send the message to this parent menu
			KeyValues *kv = new KeyValues("MenuItemSelected");
			kv->SetPtr("panel", panel);
			ivgui()->PostMessage(parentMenu->GetVPanel(), kv, GetVPanel());
		}
	}

	bool activeItemSet = false;

	FOR_EACH_LL(m_MenuItems, i)
	{
		if (m_MenuItems[i] == panel)
		{
			activeItemSet = true;
			m_iActivatedItem = i;
			break;
		}
	}
	if (!activeItemSet)
	{
		FOR_EACH_LL(m_MenuItems, i)
		{
			if (m_MenuItems[i]->HasMenu())
			{
				/*
				// GetActiveItem needs to return -1 or similar if it hasn't been set...
				if( m_MenuItems[i]->GetActiveItem() )
				{
				m_iActivatedItem = m_MenuItems[i]->GetActiveItem();
				}*/
			}
		}
	}

	// also pass it to the parent so they can respond if they like
	if (GetVParent())
	{
		KeyValues *kv = new KeyValues("MenuItemSelected");
		kv->SetPtr("panel", panel);

		ivgui()->PostMessage(GetVParent(), kv, GetVPanel());
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int Menu::GetActiveItem()
{
	return m_iActivatedItem;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
KeyValues *Menu::GetItemUserData(int itemID)
{
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		// make sure its enabled since disabled items get highlighted.
		if (menuItem && menuItem->IsEnabled())
		{
			return menuItem->GetUserData();
		}
	}
	return NULL;
}


//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void Menu::GetItemText(int itemID, wchar_t *text, int bufLenInBytes)
{
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		if (menuItem)
		{
			menuItem->GetText(text, bufLenInBytes);
			return;
		}
	}
	text[0] = 0;
}

void Menu::GetItemText(int itemID, char *text, int bufLenInBytes)
{
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		if (menuItem)
		{
			menuItem->GetText(text, bufLenInBytes);
			return;
		}
	}
	text[0] = 0;
}



//-----------------------------------------------------------------------------
// Purpose: Activate the n'th item in the menu list, as if that menu item had been selected by the user
//-----------------------------------------------------------------------------
void Menu::ActivateItem(int itemID)
{
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		// make sure its enabled since disabled items get highlighted.
		if (menuItem && menuItem->IsEnabled())
		{
			menuItem->FireActionSignal();
			m_iActivatedItem = itemID;
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void Menu::ActivateItemByRow(int row)
{
	if (m_SortedItems.IsValidIndex(row))
	{
		ActivateItem(m_SortedItems[row]);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Return the number of items currently in the menu list
//-----------------------------------------------------------------------------
int Menu::GetItemCount()
{
	return m_MenuItems.Count();
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int Menu::GetMenuID(int index)
{
	if (!m_SortedItems.IsValidIndex(index))
		return m_MenuItems.InvalidIndex();

	return m_SortedItems[index];
}

//-----------------------------------------------------------------------------
// Purpose: Return the number of items currently visible in the menu list
//-----------------------------------------------------------------------------
int Menu::GetCurrentlyVisibleItemsCount()
{
	if (m_MenuItems.Count() < m_iNumVisibleLines)
	{
		int cMenuItems = 0;
		FOR_EACH_LL(m_MenuItems, i)
		{
			if (m_MenuItems[i]->IsVisible())
			{
				++cMenuItems;
			}
		}

		return cMenuItems;
	}
	return m_iNumVisibleLines;
}

//-----------------------------------------------------------------------------
// Purpose: Enables/disables choices in the list
//			itemText - string name of item in the list 
//			state - true enables, false disables
//-----------------------------------------------------------------------------
void Menu::SetItemEnabled(const char *itemName, bool state)
{
	FOR_EACH_LL(m_MenuItems, i)
	{
		if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
		{
			m_MenuItems[i]->SetEnabled(state);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Enables/disables choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemEnabled(int itemID, bool state)
{
	if (!m_MenuItems.IsValidIndex(itemID))
		return;

	m_MenuItems[itemID]->SetEnabled(state);
}

//-----------------------------------------------------------------------------
// Purpose: shows/hides choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemVisible(const char *itemName, bool state)
{
	FOR_EACH_LL(m_MenuItems, i)
	{
		if ((Q_stricmp(itemName, m_MenuItems[i]->GetName())) == 0)
		{
			m_MenuItems[i]->SetVisible(state);
			InvalidateLayout();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: shows/hides choices in the list
//-----------------------------------------------------------------------------
void Menu::SetItemVisible(int itemID, bool state)
{
	if (!m_MenuItems.IsValidIndex(itemID))
		return;

	m_MenuItems[itemID]->SetVisible(state);
}

//-----------------------------------------------------------------------------
// Purpose: Make the scroll bar visible and narrow the menu
//			also make items visible or invisible in the list as appropriate
//-----------------------------------------------------------------------------
void Menu::AddScrollBar()
{
	m_pScroller->SetVisible(true);
	_sizedForScrollBar = true;
}

//-----------------------------------------------------------------------------
// Purpose: Make the scroll bar invisible and widen the menu
//-----------------------------------------------------------------------------
void Menu::RemoveScrollBar()
{
	m_pScroller->SetVisible(false);
	_sizedForScrollBar = false;
}

//-----------------------------------------------------------------------------
// Purpose: Invalidate layout if the slider is moved so items scroll 
//-----------------------------------------------------------------------------
void Menu::OnSliderMoved()
{
	CloseOtherMenus(NULL); // close any cascading menus

						   // Invalidate so we redraw the menu!
	InvalidateLayout();
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Toggle into mouse mode.
//-----------------------------------------------------------------------------
void Menu::OnCursorMoved(int x, int y)
{
	m_iInputMode = MOUSE;

	// chain up
	CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y));
	RequestFocus();
	InvalidateLayout();
}

//-----------------------------------------------------------------------------
// Purpose: Toggle into keyboard mode. 
//-----------------------------------------------------------------------------
void Menu::OnKeyCodePressed(KeyCode code)
{
	m_iInputMode = KEYBOARD;
	// send the message to this parent in case this is a cascading menu
	if (GetVParent())
	{
		ivgui()->PostMessage(GetVParent(), new KeyValues("KeyModeSet"), GetVPanel());
	}
}

//-----------------------------------------------------------------------------
// Purpose: Sets the item currently highlighted in the menu by ptr
//-----------------------------------------------------------------------------
void Menu::SetCurrentlySelectedItem(MenuItem *item)
{
	int itemNum = -1;
	// find it in our list of menuitems
	FOR_EACH_LL(m_MenuItems, i)
	{
		MenuItem *child = m_MenuItems[i];
		if (child == item)
		{
			itemNum = i;
			break;
		}
	}
	Assert(itemNum >= 0);

	SetCurrentlySelectedItem(itemNum);
}


//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void Menu::ClearCurrentlyHighlightedItem()
{
	if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
	{
		m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
	}
	m_iCurrentlySelectedItemID = m_MenuItems.InvalidIndex();
}

//-----------------------------------------------------------------------------
// Purpose: Sets the item currently highlighted in the menu by index
//-----------------------------------------------------------------------------
void Menu::SetCurrentlySelectedItem(int itemID)
{
	// dont deselect if its the same item
	if (itemID == m_iCurrentlySelectedItemID)
		return;

	if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
	{
		m_MenuItems[m_iCurrentlySelectedItemID]->DisarmItem();
	}

	PostActionSignal(new KeyValues("MenuItemHighlight", "itemID", itemID));
	m_iCurrentlySelectedItemID = itemID;
}

//-----------------------------------------------------------------------------
// This will set the item to be currenly selected and highlight it
// will not open cascading menu. This was added for comboboxes
// to have the combobox item highlighted in the menu when they open the
// dropdown.
//-----------------------------------------------------------------------------
void Menu::SetCurrentlyHighlightedItem(int itemID)
{
	SetCurrentlySelectedItem(itemID);
	int row = m_SortedItems.Find(itemID);
	Assert(row != -1);

	// if there is a scroll bar, and we scroll off lets move it.
	if (m_pScroller->IsVisible())
	{
		// now if we are off the scroll bar, it means we moved the scroll bar
		// by hand or set the item off the list 
		// so just snap the scroll bar straight to the item.
		if ((row >  m_pScroller->GetValue() + m_iNumVisibleLines - 1) ||
			(row < m_pScroller->GetValue()))
		{
			if (!m_pScroller->IsVisible())
				return;

			m_pScroller->SetValue(row);
		}
	}

	if (m_MenuItems.IsValidIndex(m_iCurrentlySelectedItemID))
	{
		if (!m_MenuItems[m_iCurrentlySelectedItemID]->IsArmed())
		{
			m_MenuItems[m_iCurrentlySelectedItemID]->ArmItem();
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int Menu::GetCurrentlyHighlightedItem()
{
	return m_iCurrentlySelectedItemID;
}

//-----------------------------------------------------------------------------
// Purpose: Respond to cursor entering a menuItem.
//-----------------------------------------------------------------------------
void Menu::OnCursorEnteredMenuItem(int VPanel)
{
	VPANEL menuItem = (VPANEL)VPanel;
	// if we are in mouse mode
	if (m_iInputMode == MOUSE)
	{
		MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
		// arm the menu
		item->ArmItem();
		// open the cascading menu if there is one.
		item->OpenCascadeMenu();
		SetCurrentlySelectedItem(item);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Respond to cursor exiting a menuItem
//-----------------------------------------------------------------------------
void Menu::OnCursorExitedMenuItem(int VPanel)
{
	VPANEL menuItem = (VPANEL)VPanel;
	// only care if we are in mouse mode
	if (m_iInputMode == MOUSE)
	{
		MenuItem *item = static_cast<MenuItem *>(ipanel()->GetPanel(menuItem, GetModuleName()));
		// unhighlight the item.
		// note menuItems with cascading menus will stay lit.
		item->DisarmItem();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Move up or down one in the list of items in the menu 
//			Direction is MENU_UP or MENU_DOWN
//-----------------------------------------------------------------------------
void Menu::MoveAlongMenuItemList(int direction, int loopCount)
{
	int itemID = m_iCurrentlySelectedItemID;
	int row = m_SortedItems.Find(itemID);
	row += direction;

	if (row > m_SortedItems.Count() - 1)
	{
		if (m_pScroller->IsVisible())
		{
			// stop at bottom of scrolled list
			row = m_SortedItems.Count() - 1;
		}
		else
		{
			// if no scroll bar we circle around
			row = 0;
		}
	}
	else if (row < 0)
	{
		if (m_pScroller->IsVisible())
		{
			// stop at top of scrolled list
			row = m_pScroller->GetValue();
		}
		else
		{
			// if no scroll bar circle around
			row = m_SortedItems.Count() - 1;
		}
	}

	// if there is a scroll bar, and we scroll off lets move it.
	if (m_pScroller->IsVisible())
	{
		if (row > m_pScroller->GetValue() + m_iNumVisibleLines - 1)
		{
			int val = m_pScroller->GetValue();
			val -= -direction;

			m_pScroller->SetValue(val);

			// moving the slider redraws the scrollbar,
			// and so we should redraw the menu since the
			// menu draws the black border to the right of the scrollbar.
			InvalidateLayout();
		}
		else if (row < m_pScroller->GetValue())
		{
			int val = m_pScroller->GetValue();
			val -= -direction;

			m_pScroller->SetValue(val);

			// moving the slider redraws the scrollbar,
			// and so we should redraw the menu since the
			// menu draws the black border to the right of the scrollbar.
			InvalidateLayout();
		}

		// now if we are still off the scroll bar, it means we moved the scroll bar
		// by hand and created a situation in which we moved an item down, but the
		// scroll bar is already too far down and should scroll up or vice versa
		// so just snap the scroll bar straight to the item.
		if ((row > m_pScroller->GetValue() + m_iNumVisibleLines - 1) ||
			(row < m_pScroller->GetValue()))
		{
			m_pScroller->SetValue(row);
		}
	}

	// switch it back to an itemID from row
	if (m_SortedItems.IsValidIndex(row))
	{
		SetCurrentlySelectedItem(m_SortedItems[row]);
	}

	// don't allow us to loop around more than once
	if (loopCount < m_MenuItems.Count())
	{
		// see if the text is empty, if so skip
		wchar_t text[256];
		m_MenuItems[m_iCurrentlySelectedItemID]->GetText(text, 255);
		if (text[0] == 0 || !m_MenuItems[m_iCurrentlySelectedItemID]->IsVisible())
		{
			// menu item is empty, keep moving along
			MoveAlongMenuItemList(direction, loopCount + 1);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Return which type of events the menu is currently interested in
//  MenuItems need to know because behaviour is different depending on mode.
//-----------------------------------------------------------------------------
int Menu::GetMenuMode()
{
	return m_iInputMode;
}

//-----------------------------------------------------------------------------
// Purpose: Set the menu to key mode if a child menu goes into keymode
// This mode change has to be chained up through the menu heirarchy
// so cascading menus will work when you do a bunch of stuff in keymode
// in high level menus and then switch to keymode in lower level menus.
//-----------------------------------------------------------------------------
void Menu::OnKeyModeSet()
{
	m_iInputMode = KEYBOARD;
}

//-----------------------------------------------------------------------------
// Purpose: Set the checked state of a menuItem
//-----------------------------------------------------------------------------
void Menu::SetMenuItemChecked(int itemID, bool state)
{
	m_MenuItems[itemID]->SetChecked(state);
}

//-----------------------------------------------------------------------------
// Purpose: Check if item is checked.
//-----------------------------------------------------------------------------
bool Menu::IsChecked(int itemID)
{
	return m_MenuItems[itemID]->IsChecked();
}

//-----------------------------------------------------------------------------
// Purpose: Set the minmum width the menu has to be. This
// is useful if you have a menu that is sized to the largest item in it
// but you don't want the menu to be thinner than the menu button
//-----------------------------------------------------------------------------
void Menu::SetMinimumWidth(int width)
{
	m_iMinimumWidth = width;
}

//-----------------------------------------------------------------------------
// Purpose: Get the minmum width the menu
//-----------------------------------------------------------------------------
int Menu::GetMinimumWidth()
{
	return m_iMinimumWidth;
}

//-----------------------------------------------------------------------------
// Purpose: 
// Input  :  - 
//-----------------------------------------------------------------------------
void Menu::AddSeparator()
{
	int lastID = m_MenuItems.Count() - 1;
	m_Separators.AddToTail(lastID);
	m_SeparatorPanels.AddToTail(new MenuSeparator(this, "MenuSeparator"));
}

void Menu::AddSeparatorAfterItem(int itemID)
{
	Assert(m_MenuItems.IsValidIndex(itemID));
	m_Separators.AddToTail(itemID);
	m_SeparatorPanels.AddToTail(new MenuSeparator(this, "MenuSeparator"));
}

void Menu::MoveMenuItem(int itemID, int moveBeforeThisItemID)
{
	int c = m_SortedItems.Count();
	int i;
	for (i = 0; i < c; ++i)
	{
		if (m_SortedItems[i] == itemID)
		{
			m_SortedItems.Remove(i);
			break;
		}
	}

	// Didn't find it
	if (i >= c)
	{
		return;
	}

	// Now find insert pos
	c = m_SortedItems.Count();
	for (i = 0; i < c; ++i)
	{
		if (m_SortedItems[i] == moveBeforeThisItemID)
		{
			m_SortedItems.InsertBefore(i, itemID);
			break;
		}
	}
}

void Menu::SetFont(HFont font)
{
	m_hItemFont = font;
	if (font)
	{
		m_iMenuItemHeight = surface()->GetFontTall(font) + 2;
	}
	InvalidateLayout();
}


void Menu::SetCurrentKeyBinding(int itemID, char const *hotkey)
{
	if (m_MenuItems.IsValidIndex(itemID))
	{
		MenuItem *menuItem = dynamic_cast<MenuItem *>(m_MenuItems[itemID]);
		menuItem->SetCurrentKeyBinding(hotkey);
	}
}

//-----------------------------------------------------------------------------
// Purpose: Static method to display a context menu
// Input  : *parent - 
//			*menu - 
//-----------------------------------------------------------------------------
void Menu::PlaceContextMenu(Panel *parent, Menu *menu)
{
	Assert(parent);
	Assert(menu);
	if (!menu || !parent)
		return;

	menu->SetVisible(false);
	menu->SetParent(parent);
	menu->AddActionSignalTarget(parent);

	// get cursor position, this is local to this text edit window
	int cursorX, cursorY;
	input()->GetCursorPos(cursorX, cursorY);

	menu->SetVisible(true);

	// relayout the menu immediately so that we know it's size
	menu->InvalidateLayout(true);
	int menuWide, menuTall;
	menu->GetSize(menuWide, menuTall);

	// work out where the cursor is and therefore the best place to put the menu
	int wide, tall;
	surface()->GetScreenSize(wide, tall);

	if (wide - menuWide > cursorX)
	{
		// menu hanging right
		if (tall - menuTall > cursorY)
		{
			// menu hanging down
			menu->SetPos(cursorX, cursorY);
		}
		else
		{
			// menu hanging up
			menu->SetPos(cursorX, cursorY - menuTall);
		}
	}
	else
	{
		// menu hanging left
		if (tall - menuTall > cursorY)
		{
			// menu hanging down
			menu->SetPos(cursorX - menuWide, cursorY);
		}
		else
		{
			// menu hanging up
			menu->SetPos(cursorX - menuWide, cursorY - menuTall);
		}
	}

	menu->RequestFocus();
}

void Menu::SetUseFallbackFont(bool bState, HFont hFallback)
{
	m_hFallbackItemFont = hFallback;
	m_bUseFallbackFont = bState;
}

#ifdef DBGFLAG_VALIDATE
//-----------------------------------------------------------------------------
// Purpose: Run a global validation pass on all of our data structures and memory
//			allocations.
// Input:	validator -		Our global validator object
//			pchName -		Our name (typically a member var in our container)
//-----------------------------------------------------------------------------
void Menu::Validate(CValidator &validator, char *pchName)
{
	validator.Push("vgui2::Menu", this, pchName);

	m_MenuItems.Validate(validator, "m_MenuItems");
	m_SortedItems.Validate(validator, "m_SortedItems");

	BaseClass::Validate(validator, "vgui2::Menu");

	validator.Pop();
}
#endif // DBGFLAG_VALIDATE

void Menu::PaintBackground()
{
	if (!m_bImageBackground)
		return BaseClass::PaintBackground();

	int wide, tall;
	GetSize(wide, tall);

	const int iOffset = 0;

	m_pTopBackground[0]->SetPos(0, 0);
	m_pTopBackground[0]->SetSize(7, 7);
	m_pTopBackground[0]->Paint();
	m_pTopBackground[1]->SetPos(7, 0);
	m_pTopBackground[1]->SetSize(wide - 14 - iOffset, 7);
	m_pTopBackground[1]->Paint();
	m_pTopBackground[2]->SetPos(wide - 7 - iOffset, 0);
	m_pTopBackground[2]->SetSize(7, 7);
	m_pTopBackground[2]->Paint();

	m_pCenterBackground[0]->SetPos(0, 7);
	m_pCenterBackground[0]->SetSize(7, tall - 14);
	m_pCenterBackground[0]->Paint();
	m_pCenterBackground[1]->SetPos(7, 7);
	m_pCenterBackground[1]->SetSize(wide - 14 - iOffset, tall - 14);
	m_pCenterBackground[1]->Paint();
	m_pCenterBackground[2]->SetPos(wide - 7 - iOffset, 7);
	m_pCenterBackground[2]->SetSize(7, tall - 14);
	m_pCenterBackground[2]->Paint();

	m_pBottomBackground[0]->SetPos(0, tall - 7);
	m_pBottomBackground[0]->SetSize(7, 7);
	m_pBottomBackground[0]->Paint();
	m_pBottomBackground[1]->SetPos(7, tall - 7);
	m_pBottomBackground[1]->SetSize(wide - 14 - iOffset, 7);
	m_pBottomBackground[1]->Paint();
	m_pBottomBackground[2]->SetPos(wide - 7, tall - 7);
	m_pBottomBackground[2]->SetSize(7, 7);
	m_pBottomBackground[2]->Paint();
}